package org.dromara.web.service.impl;

import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.CacheNames;
import org.dromara.common.core.constant.UserConstants;
import org.dromara.common.core.domain.dto.RoleDTO;
import org.dromara.common.core.domain.model.XcxBindLoginBody;
import org.dromara.common.core.domain.model.XcxLoginUser;
import org.dromara.common.core.enums.UserStatus;
import org.dromara.common.core.enums.UserType;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.JudgeUtils;
import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.mybatis.helper.DataPermissionHelper;
import org.dromara.common.redis.utils.CacheUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.pay.service.QrCodeService;
import org.dromara.system.domain.SysClient;
import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.bo.SysUserBo;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.service.ISysOssService;
import org.dromara.system.service.ISysPermissionService;
import org.dromara.system.service.ISysUserService;
import org.dromara.web.domain.vo.LoginVo;
import org.dromara.web.service.IAuthStrategy;
import org.dromara.web.utils.WxXcxUtil;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 小程序绑定认证策略
 *
 * @author zlj
 */
@Slf4j
@Service("xcxBind" + IAuthStrategy.BASE_NAME)
@RequiredArgsConstructor
public class XcxBindAuthStrategy implements IAuthStrategy {

    private final ISysUserService userService;
    private final SysUserMapper userMapper;
    private final ISysPermissionService permissionService;
    private final QrCodeService qrCodeService;
    private final ISysOssService ossService;

    @Override
    public LoginVo login(String body, SysClient client) {
        XcxBindLoginBody loginBody = JsonUtils.parseObject(body, XcxBindLoginBody.class);
        ValidatorUtils.validate(loginBody);
        // phoneCode 为 小程序调用 wx.getPhone 授权后获取
        String phoneCode = loginBody.getPhoneCode();
        String xcxCode = loginBody.getXcxCode();
        //获取用户信息
        WxXcxUtil.XcxLoginResp xcxLoginResp = WxXcxUtil.xcxLogin(xcxCode);
        // 多个小程序识别使用
//        String appid = loginBody.getAppid();

        //获取微信小程序绑定手机号
        String phoneNumber = WxXcxUtil.xcxGetPhoneNumber(phoneCode);
        // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可
        SysUserVo user = this.loadUserByPhone(phoneNumber, xcxLoginResp, loginBody);
        // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
        XcxLoginUser loginUser = new XcxLoginUser();
        loginUser.setTenantId(user.getTenantId());
        loginUser.setUserId(user.getUserId());
        loginUser.setUsername(user.getUserName());
        loginUser.setNickname(user.getNickName());
        loginUser.setUserType(user.getUserType());
        loginUser.setClientKey(client.getClientKey());
        loginUser.setDeviceType(client.getDeviceType());
        loginUser.setOpenid(xcxLoginResp.getOpenid());
        loginUser.setUnionid(xcxLoginResp.getUnionid());
        loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId()));
        loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId()));
        loginUser.setDeptName(ObjectUtil.isNull(user.getDept()) ? "" : user.getDept().getDeptName());
        List<RoleDTO> roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class);
        loginUser.setRoles(roles);
        SaLoginModel model = new SaLoginModel();
        model.setDevice(client.getDeviceType());
        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
        // 例如: 后台用户30分钟过期 app用户1天过期
        model.setTimeout(client.getTimeout());
        model.setActiveTimeout(client.getActiveTimeout());
        model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
        // 生成token
        LoginHelper.login(loginUser, model);

        LoginVo loginVo = new LoginVo();
        loginVo.setAccessToken(StpUtil.getTokenValue());
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
        loginVo.setClientId(client.getClientId());
        loginVo.setOpenid(xcxLoginResp.getOpenid());
        loginVo.setUnionid(xcxLoginResp.getUnionid());
        return loginVo;
    }

    private SysUserVo loadUserByPhone(String phoneNumber, WxXcxUtil.XcxLoginResp xcxLoginResp, XcxBindLoginBody loginBody) {
        // 使用 userId 查询用户
        SysUserVo user = TenantHelper.ignore(() -> userService.selectUserByPhonenumber(phoneNumber, loginBody.getTenantId()));
        if (ObjectUtil.isNull(user)) {
            //用户第一次登陆,创建新用户
            SysUserBo userBo = new SysUserBo();
            userBo.setOpenid(xcxLoginResp.getOpenid());
            userBo.setUnionid(xcxLoginResp.getUnionid());
            userBo.setTenantId(loginBody.getTenantId());
            userBo.setRoleIds(new Long[]{UserConstants.FRONT_ROLE_ID});
            userBo.setStatus(UserStatus.OK.getCode());
            userBo.setUserType(UserType.XCX_USER.getUserType());
            userBo.setNickName("用户" + RandomUtil.randomNumbers(6));
            userBo.setPhonenumber(phoneNumber);
            userBo.setDeptId(104L);
            userBo.setPostIds(new Long[]{4L});
            TenantHelper.ignore(() -> DataPermissionHelper.ignore(() -> userService.insertUser(userBo)));
            byte[] qrCodeByte = qrCodeService.xcxGetQrCode(userBo.getUserId().toString(), "pages/index/index");
            SysOssVo upload = ossService.upload(qrCodeByte);
            TenantHelper.ignore(() -> DataPermissionHelper.ignore(() ->
                userService.lambdaUpdate().set(SysUser::getQrCode, upload.getOssId()).eq(SysUser::getUserId, userBo.getUserId()).update()
            ));
            user = TenantHelper.ignore(() -> userService.selectUserByPhonenumber(phoneNumber, loginBody.getTenantId()));
        } else {
            //之前登陆过, 绑定微信唯一标识
            Long userId = user.getUserId();
            Boolean bindResult = TenantHelper.ignore(() -> userService.bindWxInfo(userId, xcxLoginResp.getOpenid(),
                xcxLoginResp.getUnionid(), loginBody.getTenantId()));
            JudgeUtils.conditionException(BooleanUtil.isFalse(bindResult)).errMsg("此手机号已和其他微信绑定");
        }
        //删除缓存的手机号
        CacheUtils.evict(CacheNames.XCX_LOGIN_RESP, loginBody.getXcxCode());
        //用户已被停用 业务逻辑自行实现
        if (ObjectUtil.isNotNull(user.getStatus()) && ObjectUtil.equal(UserStatus.DISABLE.getCode(), user.getStatus())) {
            throw new ServiceException("账号已禁用,请联系管理员");
        }
        return user;
    }

}
